package com.ruoyi.quartz.task;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.bizsys.domain.AppUser;
import com.ruoyi.bizsys.domain.ExtTryQueue;
import com.ruoyi.bizsys.domain.LoanOrder;
import com.ruoyi.bizsys.domain.RealNameAuth;
import com.ruoyi.bizsys.service.IAppUserService;
import com.ruoyi.bizsys.service.IExtTryQueueService;
import com.ruoyi.bizsys.service.ILoanOrderService;
import com.ruoyi.bizsys.service.IRealNameAuthService;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.business.common.AsyncService;
import com.ruoyi.business.service.ILoanService;
import com.ruoyi.common.config.EmailConfig;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.vo.EmailVo;
import com.ruoyi.sms.domain.SmsSendData;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 放款接口重试
 *
 * @author cyh
 */
@Slf4j
@AllArgsConstructor
@Component("loanExtApiRetry")
public class LoanExtApiRetry {

    private ILoanService loanService;

    private ILoanOrderService loanOrderService;

    private IAppUserService appUserService;

    private IExtTryQueueService tryQueueService;

    private AsyncService asyncService;

    private RedisCache redisCache;

    private IRealNameAuthService realNameAuthService;

    private static final String QUARTZ_LOAN_EXT_KEY = "Job:loan_retry_email";

    public void run() {
        log.info("放款接口重试定时任务开始执行");
        try {
            loanExtApiRetry();
        } catch (Exception e) {
            log.error("放款接口重试定时任务执行异常", e);
        }
        log.info("放款接口重试定时任务执行结束");
    }

    private void loanExtApiRetry() throws Exception {

        List<ExtTryQueue> queues = tryQueueService.list(new QueryWrapper<ExtTryQueue>(). eq("try_type", ExtTryQueue.TRANSFER).eq("state", "0").lt("try_count", 2));
        int coreSize = 4;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(coreSize, coreSize, 0L, TimeUnit.MILLISECONDS, workQueue);

        CountDownLatch latch = new CountDownLatch(queues.size());
        queues.forEach(tryTask -> {
            log.info("放款补偿线程池中队列中的线程数量：{}", workQueue.size());
            threadPoolExecutor.execute(new retryRunnable(tryTask, latch));
        });
        latch.await();
        this.sendEmail();
        threadPoolExecutor.shutdown();
    }

    private class retryRunnable implements Runnable {

        private CountDownLatch countDownLatch;

        private ExtTryQueue extTryQueue;

        private AppUser appUser;

        private LoanOrder loanOrder;

        retryRunnable(ExtTryQueue extTryQueue, CountDownLatch countDownLatch) {
            this.extTryQueue = extTryQueue;
            this.countDownLatch = countDownLatch;
            loanOrder = loanOrderService.getOne(new QueryWrapper<LoanOrder>().eq("pay_trans_serial_no", extTryQueue.getSerialNo()));
            appUser = appUserService.getOne(new QueryWrapper<AppUser>().eq("user_uuid", extTryQueue.getUserId()));
        }

        @Override
        public void run() {
            String lockKey = RedisCache.LOAN_LOCK_KEY_PREFIX + loanOrder.getLoanSerialNo();
            RLock lock = redisCache.lock(lockKey);
            try {
                loanService.startLending(loanOrder);
                //操作成功 任务结束
                tryQueueService.transferSuccess(extTryQueue.getSerialNo(), extTryQueue.getCustNo());
            } catch (Exception e) {
                boolean msgWarn = true;
                //任务结束
                if (e instanceof BusinessException) {
                    BusinessException be = (BusinessException) e;
                    log.error("放款接口重试任务异常，msg = {}, details = {}", be.getMessage(), be.getDetails());

                    //略过当前任务
                    if (RspEnmu.VERIFY_TRANSFER_NETWORK_ERROR.getMessage().equals(be.getMessage())) {
                        return;
                    }
                    if (!RspEnmu.NETWORK_BUSY.getCode().equals(be.getCode())) {
                        tryQueueService.transferSuccess(extTryQueue.getSerialNo(), extTryQueue.getCustNo());
                    }
                    //排除放款中等正常的状态
                    if (StringUtils.equalsAny(be.getCode().toString(),
                            RspEnmu.LOAN_PENDING.getCode().toString(),
                            RspEnmu.LOAN_FAILED.getCode().toString(),
                            RspEnmu.OTP_REQUEST.getCode().toString())) {
                        msgWarn = false;
                    }
                } else {
                    log.error("放款接口重试任务异常", e);
                }

                //第二次重试异常
                if (msgWarn && (extTryQueue.getTryCount() + 1) >= 2) {
                    RealNameAuth realNameAuth = realNameAuthService.getOne(new QueryWrapper<RealNameAuth>().eq("user_id", appUser.getUserUuid()));
                    //飞书提醒
                    sendFeiShu(e.getMessage(), realNameAuth.getLastName());
                    //致歉短信
                    this.apologySms(realNameAuth.getLastName());
                    //追加邮件内容
                    appendEmail(realNameAuth);
                }
            } finally {
                lock.unlock();
                countDownLatch.countDown();
            }
        }

        private void appendEmail(RealNameAuth realNameAuth) {
            StringBuilder item = new StringBuilder();
            item.append("<tr>");
            item.append("<td>" + realNameAuth.getCustNo() + "</td>");
            item.append("<td>" + loanOrder.getLoanSerialNo() + "</td>");
            item.append("<td>" + loanOrder.getPayTransSerialNo() + "</td>");
            item.append("<td>" + realNameAuth.getFullName() + "</td>");
            item.append("</tr>");
            List<String> newList = new ArrayList<>();
            newList.add(item.toString());
            redisCache.setCacheList(QUARTZ_LOAN_EXT_KEY, newList);
        }

        private void sendFeiShu(String errMsg, String fullName) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("PayTransSerialNo", extTryQueue.getSerialNo());
            jsonObject.put("Customer ID", loanOrder.getCustNo());
            jsonObject.put("Customer Name", fullName);
            jsonObject.put("Mobile", appUser.getPhone());
            String remark = tryQueueService.getOne(new QueryWrapper<ExtTryQueue>().eq("serial_no", extTryQueue.getSerialNo()).select("remark")).getRemark();
            String[] split = remark.split(",");
            jsonObject.put("Api Name", split[split.length - 1]);
            jsonObject.put("Error", errMsg);
            asyncService.sendFeishu("loan_failed", "Loan Failed", jsonObject);
        }

        private void apologySms(String lastName) {
            JSONObject param = new JSONObject();
            param.put("lastName", lastName);
            param.put("amount", loanOrder.getLoanAmt());
            SmsSendData smsData = SmsSendData.builder().noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE)
                    .phoneNumber(appUser.getPhone()).countryCode(appUser.getCountryCode())
                    .smsParam(param).smsType(BizConstants.SMS_TEMPLATE_WDRW_FAIL_SRY).build();
            asyncService.sendSms(smsData);
        }
    }

    private void sendEmail() {
        List<String> cacheList = redisCache.getCacheList(QUARTZ_LOAN_EXT_KEY);
        if (cacheList.size() == 0) {
            return;
        }
        StringBuilder content = new StringBuilder();
        content.append("<html><head></head><body>Dear all,<br/>以下订单因接口问题无法放款，请检查订单是否已放款成功，并跟进线下处理流程，及时处理：<br/>");
        content.append("<table border=1><tr><td>CustomerID</td><td>LoanSerialNo</td><td>PayTransSerialNo</td><td>CustomerName</td></tr>");
        content.append(String.join("", cacheList));
        content.append("</table></body></html>");

        EmailVo emailVo = EmailVo.builder()
                .subject("Loan Failed")
                .cc(EmailConfig.getLoanCompensateCc().split(","))
                .addressees(EmailConfig.getLoanCompensateAddress().split(","))
                .content(content.toString())
                .build();
        asyncService.sendCustomizeEmail(emailVo);
        redisCache.deleteObject(QUARTZ_LOAN_EXT_KEY);
    }

}

